#pragma once

#include "base.h"
#include "../impl/count.h"

namespace mdtx::online::updater
{
    template <typename T>
    class count_nz : public counter_base1<T>,
                     public impl::count_nz<T>
    {
        using base = counter_base1<T>;
        using ibase = impl::count_nz<T>;

    public:
        using typename base::tuple_in;
        using typename base::tuple_out;
        using base::insert;
        using base::remove;

        using ibase::insert_impl;
        using ibase::remove_impl;
        using ibase::reset_impl;

        void insert(tuple_in const &in) override
        {
            insert_impl(std::get<0>(in));
        }

        void remove(tuple_in const &in) override
        {
            remove_impl(std::get<0>(in));
        }

        void reset() override
        {
            reset_impl();
        }

        tuple_out get() override
        {
            return tuple_out(this->m);
        }
    };

    template <typename T>
    class any_nz : public counter_base1<T>,
                   public impl::count_nz<T>
    {
        using base = counter_base1<T>;
        using ibase = impl::count_nz<T>;

    public:
        using typename base::tuple_in;
        using typename base::tuple_out;
        using base::insert;
        using base::remove;

        using ibase::insert_impl;
        using ibase::remove_impl;
        using ibase::reset_impl;

        void insert(tuple_in const &in) override
        {
            insert_impl(std::get<0>(in));
        }

        void remove(tuple_in const &in) override
        {
            remove_impl(std::get<0>(in));
        }

        void reset() override
        {
            reset_impl();
        }

        tuple_out get() override
        {
            return tuple_out(this->m > 0);
        }
    };

    template <typename T>
    class all_nz : public counter_base1<T>,
                   public impl::count_nz<T>
    {
        using base = counter_base1<T>;
        using ibase = impl::count_nz<T>;

    public:
        using typename base::tuple_in;
        using typename base::tuple_out;
        using base::insert;
        using base::remove;

        using ibase::insert_impl;
        using ibase::remove_impl;
        using ibase::reset_impl;

        void insert(tuple_in const &in) override
        {
            insert_impl(std::get<0>(in));
        }

        void remove(tuple_in const &in) override
        {
            remove_impl(std::get<0>(in));
        }

        void reset() override
        {
            reset_impl();
        }

        tuple_out get() override
        {
            return tuple_out(this->m && this->m == this->n);
        }
    };

    template <typename T>
    class count_n : public counter_base1<T>,
                    public impl::count_n<int>
    {
        using base = counter_base1<T>;
        using ibase = impl::count_n<int>;

    public:
        using typename base::tuple_in;
        using typename base::tuple_out;
        using base::insert;
        using base::remove;

        using ibase::insert_impl;
        using ibase::remove_impl;
        using ibase::reset_impl;

        void insert(tuple_in const &) override
        {
            insert_impl(0);
        }

        void remove(tuple_in const &) override
        {
            remove_impl(0);
        }

        void reset() override
        {
            reset_impl();
        }

        tuple_out get() override
        {
            return tuple_out(this->n);
        }
    };
}
